using System;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using BoxLabeling.Properties;
using System.Collections;

namespace BoxLabeling
{
	public partial class frmBoxLabeling : Form
	{
		public double scale_SrcImage;
		public Bitmap img_SrcImage;
		public Bitmap img_Scaled_SrcImage;
		public Bitmap img_ToShow_in_PixBox;
		SortedDictionary<CObjectLabel, CObjectLabel> Dict;

		public class CObjectLabel : ICloneable, IComparable
		{
			public int image_index;
			public int in_image_index;
			public int x, y, w, h;
			public int difficulty;

			#region IComparable Members

			public int CompareTo(object obj)
			{
				CObjectLabel another = (CObjectLabel)obj;
				int t; 
				t = image_index - another.image_index;
				if (t != 0) return t;
				t = in_image_index - another.in_image_index;
				if (t != 0) return t;
				return 0;
			}

			#endregion

			#region ICloneable Members

			public object Clone()
			{
				CObjectLabel ret = new CObjectLabel();
				ret.image_index = image_index;
				ret.in_image_index = in_image_index;
				ret.x = x;
				ret.y = y;
				ret.w = w;
				ret.h = h;
				ret.difficulty = difficulty;
				return ret;
			}

			#endregion
		}

		private void InitDataMembers()
		{
			//List_ObjectLabel = new ArrayList();
			Dict = new SortedDictionary<CObjectLabel, CObjectLabel>();

			LoadLabelList(Settings.Default.strFN_DstLabelList);
		}

		private void FinishDataMembers()
		{
			Settings.Default.Save();
			SaveLabelList(Settings.Default.strFN_DstLabelList);
		}

		private CObjectLabel GetObjectLabelFromDict(int image_index, int annotation_index)
		{
			CObjectLabel ret = new CObjectLabel();
			ret.image_index = image_index;
			ret.in_image_index = annotation_index;
			if (Dict.ContainsKey(ret))
				return Dict[ret];
			else
				return null;
		}

		private CObjectLabel GetCurrentObjectLabelFromDict()
		{
			return GetObjectLabelFromDict(
				listBox_SrcImage.SelectedIndex,
				listBox_ObjectAnnotation.SelectedIndex);
				//Settings.Default.si_LastImage,
				//Settings.Default.si_LastAnnotation);
		}

		private void LoadLabelList(string strFN_LabelList)
		{
			Dict.Clear();
			StreamReader FIN = new StreamReader(strFN_LabelList);
			string str_line;
			CObjectLabel tBox;
			int ii;

			str_line = FIN.ReadLine();
			for (; ; )
			{
				str_line = FIN.ReadLine();
				if (str_line == null) break;

				tBox = new CObjectLabel();
				ii = 0;
				string[] sp = str_line.Split(new char[] { '\r', '\n', '\t', ' ' });

				while (sp[ii] == "") ++ii;
				tBox.image_index = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.in_image_index = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.x = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.y = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.w = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.h = Int32.Parse(sp[ii++]);

				while (sp[ii] == "") ++ii;
				tBox.difficulty = Int32.Parse(sp[ii++]);

				Dict[tBox] = (CObjectLabel)tBox.Clone();
			}
			FIN.Close();
		}

		private void SaveLabelList(string strFN_LabelList)
		{
			StreamWriter FOUT = new StreamWriter(strFN_LabelList);
			CObjectLabel tBox;
			string str_line;

			str_line = Dict.Count.ToString();
			FOUT.WriteLine(str_line);
			foreach (KeyValuePair<CObjectLabel, CObjectLabel> kvp in Dict)
			{
				tBox = kvp.Value;
				str_line = "";
				str_line += "\t" + tBox.image_index.ToString();
				str_line += "\t" + tBox.in_image_index.ToString();
				str_line += "\t" + tBox.x.ToString();
				str_line += "\t" + tBox.y.ToString();
				str_line += "\t" + tBox.w.ToString();
				str_line += "\t" + tBox.h.ToString();
				str_line += "\t" + tBox.difficulty.ToString();
				FOUT.WriteLine(str_line);
			}
			FOUT.Close();
		}

		private void ResetButtonsSize()
		{
			int margin = 1;
			int w0 = pnlLabelingButtons.Width;
			int h0 = pnlLabelingButtons.Height;
			int w1 = (int)(pnlLabelingButtons.Width * 0.25);
			int h1 = (int)(pnlLabelingButtons.Height * 1.00 / 3);

			btnLast.Left = w0 - w1 - w1 + margin; btnLast.Top = margin;
			btnLast.Width = w1 - margin * 2;
			btnLast.Height = h0 - margin * 2;

			btnNext.Left = w0 - w1 + margin; btnNext.Top = margin;
			btnNext.Width = w1 - margin * 2;
			btnNext.Height = h0 - margin * 2;

			btnEasy.Left = margin; btnEasy.Top = margin;
			btnEasy.Width = w0 - w1 * 2 - margin * 2;
			btnEasy.Height = h1 - margin * 2;

			btnHard.Left = margin; btnHard.Top = h1 + margin;
			btnHard.Width = w0 - w1 - w1 - margin * 2;
			btnHard.Height = h1 - margin * 2;

			btnUnknown.Left = margin; btnUnknown.Top = h1 + h1 + margin;
			btnUnknown.Width = w0 - w1 - w1 - margin * 2;
			btnUnknown.Height = h0 - h1 - h1 - margin * 2;
		}

		private void RefreshSrcList()
		{
			listBox_SrcImage.ClearSelected();
			listBox_SrcImage.Items.Clear();

			img_SrcImage = null;
			int i;
			for (i = 0; i < Settings.Default.Amount_SrcImage; ++i)
			{
				listBox_SrcImage.Items.Add(i.ToString("D4"));
			}
			
			listBox_SrcImage.SetSelected(0, true);
			listBox_ObjectAnnotation.SetSelected(0, true);
		}

		private void Repaint_PicBox()
		{
			double sx = 1.0 * (pnlWorkSpace.Width - 5) / img_SrcImage.Width;
			double sy = 1.0 * (pnlWorkSpace.Height - 5) / img_SrcImage.Height;
			scale_SrcImage = (sx < sy ? sx : sy);
			if (scale_SrcImage > 1.0) scale_SrcImage = 1.0;

			img_Scaled_SrcImage = (Bitmap)img_SrcImage.GetThumbnailImage(
				(int)(scale_SrcImage * img_SrcImage.Width),
				(int)(scale_SrcImage * img_SrcImage.Height),
				null, IntPtr.Zero);

			Repaint_BoundingBox();
		}

		private void Repaint_BoundingBox()
		{
			if (img_Scaled_SrcImage == null) return;

			img_ToShow_in_PixBox = (Bitmap)img_Scaled_SrcImage.Clone();
			picSrcImage.Image = img_ToShow_in_PixBox;

			CObjectLabel tBox = GetCurrentObjectLabelFromDict();
			if (tBox == null) return;

			int xL = tBox.x;
			int xH = tBox.x + tBox.w - 1;
			int yL = tBox.y;
			int yH = tBox.y + tBox.h - 1;

			xL = (int)(xL * scale_SrcImage);
			xH = (int)(xH * scale_SrcImage);
			yL = (int)(yL * scale_SrcImage);
			yH = (int)(yH * scale_SrcImage);

			Graphics g = Graphics.FromImage(img_ToShow_in_PixBox);
			Color tc = Color.Yellow;

			if (tBox.difficulty == 0) tc = Color.FromArgb(255, 255, 0);
			if (tBox.difficulty == 1) tc = Color.FromArgb(0, 255, 0);
			if (tBox.difficulty == 2) tc = Color.FromArgb(255, 0, 0);
			Pen pen = new Pen(tc, 2);
			g.DrawLine(pen, xL, yL, xH, yL);
			g.DrawLine(pen, xL, yL, xL, yH);
			g.DrawLine(pen, xH, yH, xH, yL);
			g.DrawLine(pen, xH, yH, xL, yH);

			picSrcImage.Image = img_ToShow_in_PixBox;
		}

		private void LoadImageAnnotation(string strFN_SrcAnnotation)
		{
			listBox_ObjectAnnotation.ClearSelected();
			listBox_ObjectAnnotation.Items.Clear();

			StreamReader FIN = new StreamReader(strFN_SrcAnnotation);
			string str_line;
			int iii = 0;
			for (; ; )
			{
				str_line = FIN.ReadLine();
				if (str_line == null) break;

				int z0, z1;
				int xL, yL, xH, yH;

				z0 = str_line.IndexOf("Ymax) : (");
				if (z0 < 0) continue;
				z0 += 9;
				z1 = str_line.IndexOf(",", z0);
				if (z1 < 0) continue;

				xL = Int32.Parse(str_line.Substring(z0, z1 - z0));

				z0 = z1 + 1;
				z1 = str_line.IndexOf(") - (", z0);
				if (z1 < 0) continue;

				yL = Int32.Parse(str_line.Substring(z0, z1 - z0));

				z0 = z1 + 5;
				z1 = str_line.IndexOf(",", z0);
				if (z1 < 0) continue;

				xH = Int32.Parse(str_line.Substring(z0, z1 - z0));

				z0 = z1 + 1;
				z1 = str_line.IndexOf(")", z0);
				if (z1 < 0) continue;

				yH = Int32.Parse(str_line.Substring(z0, z1 - z0));

				CObjectLabel tBox = new CObjectLabel();
				tBox.image_index = listBox_SrcImage.SelectedIndex;
				tBox.in_image_index = iii++;
				--xL; --yL;
				--xH; --yH;
				tBox.x = xL; tBox.y = yL;
				tBox.w = xH - xL + 1; tBox.h = yH - yL + 1;
				tBox.difficulty = 0;

				if (!(Dict.ContainsKey(tBox)))
					Dict[tBox] = (CObjectLabel)tBox.Clone();

				str_line =
					"[" + xL.ToString() +
					"," + yL.ToString() +
					"] - [" + xH.ToString() +
					"," + yH.ToString() +
					"]";
				listBox_ObjectAnnotation.Items.Add(str_line);

				Console.WriteLine(str_line);
			}
			FIN.Close();

			//listBox_ObjectAnnotation.SetSelected(0, true);
		}

		// ==================================================

		public frmBoxLabeling()
		{
			InitializeComponent();
		}

		private void frmBoxLabeling_Load(object sender, EventArgs e)
		{
			InitDataMembers();

			txtPN_SrcImage.Text = Settings.Default.strPN_SrcImage;
			txtPN_SrcAnnotation.Text = Settings.Default.strPN_SrcAnnotation;
			txtFN_DstLabelList.Text = Settings.Default.strFN_DstLabelList;
			txtAmount_SrcImage.Text = Settings.Default.Amount_SrcImage.ToString();

			int si_0 = Settings.Default.si_LastImage;
			int si_1 = Settings.Default.si_LastAnnotation;

			ResetButtonsSize();
			RefreshSrcList();

			listBox_SrcImage.SetSelected(si_0, true);
			listBox_ObjectAnnotation.SetSelected(si_1, true);
		}

		private void frmBoxLabeling_FormClosed(object sender, FormClosedEventArgs e)
		{
			FinishDataMembers();
		}

		private void txtPN_SrcImage_TextChanged(object sender, EventArgs e)
		{
			Settings.Default.strPN_SrcImage = txtPN_SrcImage.Text;
		}

		private void txtPN_SrcAnnotation_TextChanged(object sender, EventArgs e)
		{
			Settings.Default.strPN_SrcAnnotation = txtPN_SrcAnnotation.Text;
		}

		private void txtFN_DstLabelList_TextChanged(object sender, EventArgs e)
		{
			Settings.Default.strFN_DstLabelList = txtFN_DstLabelList.Text;
		}

		private void txtAmount_SrcImage_TextChanged(object sender, EventArgs e)
		{
			int tt;
			try
			{
				tt = Int32.Parse(txtAmount_SrcImage.Text);
				Settings.Default.Amount_SrcImage = tt;
			}
			catch //(System.Exception err)
			{
				{

				}
			}
		}

		private void btnRefreshSrcList_Click(object sender, EventArgs e)
		{
			RefreshSrcList();
		}

		private void pnlWorkSpace_SizeChanged(object sender, EventArgs e)
		{
			if (img_SrcImage == null)
			{
				picSrcImage.Image = null;
				return;
			}
			Repaint_PicBox();
		}

		private void listBox_SrcImage_SelectedIndexChanged(object sender, EventArgs e)
		{
			int si = listBox_SrcImage.SelectedIndex;
			if (si < 0 || si >= listBox_SrcImage.Items.Count) return;

			Settings.Default.si_LastImage = si;

			string strFN_SrcImage =
				Settings.Default.strPN_SrcImage + listBox_SrcImage.SelectedItem + ".jpg";
			img_SrcImage = (Bitmap)Bitmap.FromFile(strFN_SrcImage);

			string strFN_SrcAnnotation =
				Settings.Default.strPN_SrcAnnotation + listBox_SrcImage.SelectedItem + ".txt";
			LoadImageAnnotation(strFN_SrcAnnotation);

			Repaint_PicBox();
		}

		private void listBox_ObjectAnnotation_SelectedIndexChanged(object sender, EventArgs e)
		{
			int si = listBox_ObjectAnnotation.SelectedIndex;
			if (si < 0 || si >= listBox_ObjectAnnotation.Items.Count) return;

			Settings.Default.si_LastAnnotation = si;

			Repaint_BoundingBox();
		}

		private void pnlLabelingButtons_SizeChanged(object sender, EventArgs e)
		{
			ResetButtonsSize();
		}

		private void btnNext_Click(object sender, EventArgs e)
		{
			bool ok;
			int s1, s2;
			s1 = listBox_ObjectAnnotation.SelectedIndex;
			s2 = listBox_SrcImage.SelectedIndex;

			ok = (s1 >= 0) && (s1 < listBox_ObjectAnnotation.Items.Count - 1);
			if (ok)
			{
				++s1;
				listBox_ObjectAnnotation.SetSelected(s1, true);
				return;
			}

			if ((listBox_ObjectAnnotation.Items.Count > 0) &&
				(s1 < 0 || s1 >= listBox_ObjectAnnotation.Items.Count))
			{
				s1 = 0;
				listBox_ObjectAnnotation.SetSelected(s1, true);
				return;
			}

			for (; ; )
			{
				ok = (s2 >= 0) && (s2 < listBox_SrcImage.Items.Count - 1);
				if (!ok) return;

				++s2;
				listBox_SrcImage.SetSelected(s2, true);
				if (listBox_ObjectAnnotation.Items.Count > 0)
				{
					s1 = 0;
					listBox_ObjectAnnotation.SetSelected(s1, true);
					break;
				}
			}
		}

		private void btnLast_Click(object sender, EventArgs e)
		{
			bool ok;
			int s1, s2;
			s1 = listBox_ObjectAnnotation.SelectedIndex;
			s2 = listBox_SrcImage.SelectedIndex;

			ok = (s1 > 0);
			if (ok)
			{
				--s1;
				listBox_ObjectAnnotation.SetSelected(s1, true);
				return;
			}

			if ((listBox_ObjectAnnotation.Items.Count > 0) &&
				(s1 < 0 || s1 >= listBox_ObjectAnnotation.Items.Count))
			{
				s1 = listBox_ObjectAnnotation.Items.Count - 1;
				listBox_ObjectAnnotation.SetSelected(s1, true);
				return;
			}

			for (; ; )
			{
				ok = (s2 > 0);
				if (!ok) return;

				--s2;
				listBox_SrcImage.SetSelected(s2, true);
				if (listBox_ObjectAnnotation.Items.Count > 0)
				{
					s1 = listBox_ObjectAnnotation.Items.Count - 1;
					listBox_ObjectAnnotation.SetSelected(s1, true);
					break;
				}
			}
		}

		private void btnEasy_Click(object sender, EventArgs e)
		{
			CObjectLabel tBox = GetCurrentObjectLabelFromDict();
			if (tBox == null) return;
			tBox.difficulty = 1;
			Repaint_BoundingBox();
		}

		private void btnHard_Click(object sender, EventArgs e)
		{
			CObjectLabel tBox = GetCurrentObjectLabelFromDict();
			if (tBox == null) return;
			tBox.difficulty = 2;
			Repaint_BoundingBox();
		}

		private void btnUnknown_Click(object sender, EventArgs e)
		{
			CObjectLabel tBox = GetCurrentObjectLabelFromDict();
			if (tBox == null) return;
			tBox.difficulty = 0;
			Repaint_BoundingBox();
		}

		private void btnLoadLabelList_Click(object sender, EventArgs e)
		{
			LoadLabelList(Settings.Default.strFN_DstLabelList);
		}

		private void btnSaveLabelList_Click(object sender, EventArgs e)
		{
			SaveLabelList(Settings.Default.strFN_DstLabelList);
		}
	}
}

